<?php
require_once __DIR__ . '/Logger.php';

/**
 * HTML解析类
 * 用于解析神马搜索页面并提取关联关键词
 */
class HtmlParser
{
    private $logger;
    private $config;
    
    public function __construct($config = null)
    {
        $this->config = $config;
        $this->logger = new Logger($config);
    }
    
    /**
     * 获取URL的HTML内容
     * @param string $url URL地址
     * @return string|false HTML内容
     */
    public function fetchHtml($url)
    {
        $this->logger->info('开始获取URL内容', ['url' => $url]);
        
        try {
            // 设置请求选项
            $options = [
                'http' => [
                    'method' => 'GET',
                    'header' => [
                        'User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
                        'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                        'Accept-Language: zh-CN,zh;q=0.9',
                        'Accept-Encoding: gzip, deflate',
                        'Connection: keep-alive'
                    ],
                    'timeout' => 30,
                    'follow_location' => true,
                    'max_redirects' => 5
                ],
                'ssl' => [
                    'verify_peer' => false,
                    'verify_peer_name' => false
                ]
            ];
            
            $context = stream_context_create($options);
            $html = @file_get_contents($url, false, $context);
            
            if ($html === false) {
                $this->logger->error('获取URL内容失败', ['url' => $url]);
                
                // 尝试使用curl
                if (function_exists('curl_init')) {
                    $this->logger->info('尝试使用CURL获取内容');
                    $html = $this->fetchHtmlWithCurl($url);
                }
            }
            
            if ($html !== false) {
                $htmlLength = strlen($html);
                $this->logger->info('成功获取HTML内容', ['length' => $htmlLength]);
                
                // 检查是否是gzip压缩的内容
                if (substr($html, 0, 2) === "\x1f\x8b") {
                    $this->logger->info('检测到gzip压缩内容，开始解压');
                    $decodedHtml = @gzdecode($html);
                    if ($decodedHtml !== false) {
                        $html = $decodedHtml;
                        $this->logger->info('gzip解压成功', ['new_length' => strlen($html)]);
                    } else {
                        $this->logger->warning('gzip解压失败，使用原始内容');
                    }
                }
                
                // 记录HTML的前500个字符用于调试
                $this->logger->debug('HTML内容预览', ['preview' => substr($html, 0, 500)]);
                
                // 根据配置决定是否将完整的HTML保存到文件
                if ($this->config && isset($this->config['save_html_file']) && $this->config['save_html_file']) {
                    $htmlFile = __DIR__ . '/../return.html';
                    try {
                        $result = @file_put_contents($htmlFile, $html);
                        if ($result !== false) {
                            $this->logger->info('HTML已保存到文件', [
                                'file' => $htmlFile, 
                                'bytes' => $result,
                                'realpath' => realpath($htmlFile)
                            ]);
                        } else {
                            $this->logger->error('保存HTML文件失败', [
                                'file' => $htmlFile,
                                'dir_writable' => is_writable(dirname($htmlFile)),
                                'dir_exists' => is_dir(dirname($htmlFile))
                            ]);
                        }
                    } catch (Exception $e) {
                        $this->logger->error('保存HTML文件异常', ['error' => $e->getMessage()]);
                    }
                } else {
                    $this->logger->debug('未启用HTML文件保存功能');
                }
            }
            
            return $html;
            
        } catch (Exception $e) {
            $this->logger->error('获取HTML内容异常', ['error' => $e->getMessage()]);
            return false;
        }
    }
    
    /**
     * 使用CURL获取HTML内容
     * @param string $url URL地址
     * @return string|false HTML内容
     */
    private function fetchHtmlWithCurl($url)
    {
        $ch = curl_init();
        
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS => 5,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
            CURLOPT_HTTPHEADER => [
                'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Language: zh-CN,zh;q=0.9',
                'Accept-Encoding: gzip, deflate',
                'Connection: keep-alive'
            ],
            CURLOPT_ENCODING => 'gzip, deflate'
        ]);
        
        $html = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        
        curl_close($ch);
        
        if ($html === false || $httpCode !== 200) {
            $this->logger->error('CURL获取失败', [
                'http_code' => $httpCode,
                'error' => $error
            ]);
            return false;
        }
        
        $this->logger->info('CURL获取成功', ['http_code' => $httpCode]);
        return $html;
    }
    
    /**
     * 解析HTML并提取关联关键词（简化接口）
     * @param string $html HTML内容
     * @return array 关联关键词数组
     */
    public function parseKeywords($html)
    {
        return $this->parseRelatedKeywords($html);
    }
    
    /**
     * 解析HTML并提取关联关键词
     * @param string $html HTML内容
     * @return array 关联关键词数组
     */
    public function parseRelatedKeywords($html)
    {
        $this->logger->info('开始解析关联关键词');
        
        if (empty($html)) {
            $this->logger->warning('HTML内容为空，无法解析');
            return [];
        }
        
        $keywords = [];
        
        try {
            // 禁用错误输出
            libxml_use_internal_errors(true);
            
            // 创建DOMDocument对象
            $dom = new DOMDocument();
            
            // 转换编码
            $html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
            
            // 加载HTML
            $dom->loadHTML($html);
            
            // 创建DOMXPath对象
            $xpath = new DOMXPath($dom);
            
            $this->logger->debug('DOMDocument创建成功，开始查找关联关键词');
            
            // 只使用完整的XPath路径：aside#sider > div.sider-cards > div.sider-card.relative-keywords > ul > li
            $xpathQuery = "//aside[@id='sider']/div[contains(@class, 'sider-cards')]/div[contains(@class, 'sider-card') and contains(@class, 'relative-keywords')]/ul/li";
            
            $this->logger->debug('使用完整XPath路径查询', ['xpath' => $xpathQuery]);
            $nodes = $xpath->query($xpathQuery);
            
            if ($nodes && $nodes->length > 0) {
                $this->logger->info('XPath查询成功', ['node_count' => $nodes->length]);
            } else {
                $this->logger->warning('未找到关联关键词元素，不保存到数据库');
                return []; // 如果没找到，直接返回空数组
            }
            
            if ($nodes && $nodes->length > 0) {
                foreach ($nodes as $node) {
                    $keyword = null;
                    
                    // 方法1: 尝试 a > p > span.news-title (神马搜索标准结构)
                    $spans = $xpath->query(".//a/p/span[contains(@class, 'news-title')]", $node);
                    if ($spans && $spans->length > 0) {
                        $keyword = trim($spans->item(0)->textContent);
                        $this->logger->debug('找到关键词(span.news-title)', ['keyword' => $keyword]);
                    }
                    
                    // 方法2: 尝试 a > p > span (任意span)
//                    if (empty($keyword)) {
//                        $spans = $xpath->query(".//a/p/span", $node);
//                        if ($spans && $spans->length > 0) {
//                            $keyword = trim($spans->item(0)->textContent);
//                            $this->logger->debug('找到关键词(a>p>span)', ['keyword' => $keyword]);
//                        }
//                    }
//
//                    // 方法3: 尝试 span.news-title (直接查找)
//                    if (empty($keyword)) {
//                        $spans = $xpath->query(".//span[contains(@class, 'news-title')]", $node);
//                        if ($spans && $spans->length > 0) {
//                            $keyword = trim($spans->item(0)->textContent);
//                            $this->logger->debug('找到关键词(span.news-title直接)', ['keyword' => $keyword]);
//                        }
//                    }
//
//                    // 方法4: 尝试 a > span
//                    if (empty($keyword)) {
//                        $spans = $xpath->query(".//a/span", $node);
//                        if ($spans && $spans->length > 0) {
//                            $keyword = trim($spans->item(0)->textContent);
//                            $this->logger->debug('找到关键词(a>span)', ['keyword' => $keyword]);
//                        }
//                    }
//
//                    // 方法5: 尝试 a 标签
//                    if (empty($keyword)) {
//                        $links = $xpath->query(".//a", $node);
//                        if ($links && $links->length > 0) {
//                            $keyword = trim($links->item(0)->textContent);
//                            $this->logger->debug('找到关键词(a)', ['keyword' => $keyword]);
//                        }
//                    }
//
//                    // 方法6: 直接获取li的文本
//                    if (empty($keyword)) {
//                        $keyword = trim($node->textContent);
//                        $this->logger->debug('找到关键词(li)', ['keyword' => $keyword]);
//                    }
                    
                    // 过滤并添加关键词
                    if (!empty($keyword) && mb_strlen($keyword) > 0 && mb_strlen($keyword) < 100) {
                        $keywords[] = $keyword;
                        $this->logger->debug('成功添加关联关键词', ['keyword' => $keyword]);
                    }
                }
            }
            
            // 去重
            $keywords = array_unique($keywords);
            
            $this->logger->info('关联关键词解析完成', [
                'total_count' => count($keywords),
                'keywords' => $keywords
            ]);
            
            // 清理错误
            libxml_clear_errors();
            
        } catch (Exception $e) {
            $this->logger->error('解析关联关键词异常', ['error' => $e->getMessage()]);
        }
        
        return array_values($keywords);
    }
    
    /**
     * 获取并解析关联关键词（一步到位）
     * @param string $url URL地址
     * @return array 关联关键词数组
     */
    public function fetchAndParseKeywords($url)
    {
        $this->logger->info('=== 开始获取并解析关联关键词 ===', ['url' => $url]);
        
        $html = $this->fetchHtml($url);
        
        if ($html === false) {
            $this->logger->error('获取HTML失败，无法继续解析');
            return [];
        }
        
        $keywords = $this->parseRelatedKeywords($html);
        
        $this->logger->info('=== 获取并解析关联关键词完成 ===', ['count' => count($keywords)]);
        
        return $keywords;
    }
}
